/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import { AbstractBinopExpr, AbstractExpr, AbstractFieldRef, AbstractInvokeExpr, AbstractRef, ArkArrayRef, ArkAwaitExpr, ArkCastExpr, ArkDeleteExpr, ArkInstanceFieldRef, ArkInstanceInvokeExpr, ArkInstanceOfExpr, ArkNewArrayExpr, ArkNewExpr, ArkParameterRef, ArkPhiExpr, ArkPtrInvokeExpr, ArkTypeOfExpr, ArkUnopExpr, ArkYieldExpr, Constant, Local, Value } from "../../ArkAnalyzer/src";
import { MethodParameter } from "../../ArkAnalyzer/src/core/model/builder/ArkMethodBuilder";
import { JsonValue } from "./jsonValue";
import { parseArkType } from "./types";

export class BasicValue {
    protected json: JsonValue = new JsonValue();

    constructor(arkValue: Value){
        this.json.setIdentifier(arkValue.constructor.name);
        this.json.setType(parseArkType(arkValue.getType()))
    }

    public getJson(): Record<string, any>{
        return this.json.getJson();
    }
}

export function parseArkValue(arkValue: Value): Record<string, any> {
    let value: BasicValue;
    if (arkValue instanceof Local) {
        value = new LocalValue(arkValue);
    }
    else if (arkValue instanceof AbstractRef){
        value = parseAbstractRef(arkValue);
    }
    else if (arkValue instanceof Constant) {
        value = new ConstantValue(arkValue);
    }
    else if (arkValue instanceof AbstractExpr){
        value = parseAbstractExpr(arkValue);
    }
    else if (arkValue instanceof MethodParameter){
        value = new MethodParameterValue(arkValue);
    }
    else {
        value = new BasicValue(arkValue);
    }
    return value.getJson();
}

function parseAbstractRef(abstractRef: AbstractRef): AbstractRefValue{
    if (abstractRef instanceof ArkArrayRef){
        return new ArkArrayRefValue(abstractRef);
    }
    else if (abstractRef instanceof AbstractFieldRef){
        if (abstractRef instanceof ArkInstanceFieldRef){
            return new ArkInstanceFieldRefValue(abstractRef);
        }
        return new AbstractFieldRefValue(abstractRef);
    }
    else if (abstractRef instanceof ArkParameterRef){
        return new ArkParameterRefValue(abstractRef);
    }
    else {
        return new AbstractRefValue(abstractRef);
    }
}

function parseAbstractExpr(abstractExpr: AbstractExpr): AbstractExprValue {
    if(abstractExpr instanceof AbstractInvokeExpr) {
        return parseAbstractInvokeExpr(abstractExpr);
    }
    else if (abstractExpr instanceof ArkNewExpr){
        return new ArkNewExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkNewArrayExpr) {
        return new ArkNewArrayExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkDeleteExpr){
        return new ArkDeleteExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkAwaitExpr){
        return new ArkAwaitExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkYieldExpr){
        return new ArkYieldExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof AbstractBinopExpr){
        return new AbstractBinopExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkTypeOfExpr){
        return new ArkTypeOfExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkInstanceOfExpr){
        return new ArkInstanceOfExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkCastExpr){
        return new ArkCastExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkPhiExpr){
        return new ArkPhiExprValue(abstractExpr);
    }
    else if (abstractExpr instanceof ArkUnopExpr){
        return new ArkUnopExprValue(abstractExpr);
    }
    else {
        return new AbstractExprValue(abstractExpr);
    }
}

function parseAbstractInvokeExpr(abstractInvokeExpr: AbstractInvokeExpr): AbstractInvokeExprValue{
    if (abstractInvokeExpr instanceof ArkInstanceInvokeExpr){
        return new ArkInstanceInvokeExprValue(abstractInvokeExpr);
    }
    else if (abstractInvokeExpr instanceof ArkPtrInvokeExpr) {
        return new ArkPtrInvokeExprValue(abstractInvokeExpr);
    }
    else{
        return new AbstractInvokeExprValue(abstractInvokeExpr);
    }
}

class MethodParameterValue extends BasicValue {
    constructor(methodParameter: MethodParameter) {
        super(methodParameter);
        this.parse(methodParameter);
    }

    private parse(methodParameter: MethodParameter) {
        this.json.setName(methodParameter.getName());
    }
}

class LocalValue extends BasicValue {
    constructor(arkLocal: Local) {
        super(arkLocal);
        this.parse(arkLocal);
    }

    private parse(arkLocal: Local) {
        this.json.setName(arkLocal.getName());
    }
}

class AbstractRefValue extends BasicValue {
    constructor(arkAbstractRef: AbstractRef) {
        super(arkAbstractRef);
    }
}

class ArkArrayRefValue extends AbstractRefValue {
    constructor(arkArrayRef: ArkArrayRef) {
        super(arkArrayRef);
        this.parse(arkArrayRef);
    }

    private parse(arkArrayRef: ArkArrayRef) {
        this.json.setBase(parseArkValue(arkArrayRef.getBase()));
        this.json.setIndex(parseArkValue(arkArrayRef.getIndex()));
    }
}

class AbstractFieldRefValue extends AbstractRefValue {
    constructor(abstractFieldRef: AbstractFieldRef) {
        super(abstractFieldRef);
        this.json.setName(abstractFieldRef.getFieldName());
        this.json.setBase(abstractFieldRef.getFieldSignature().getBaseName());
    }
}

class ArkInstanceFieldRefValue extends AbstractFieldRefValue {
    constructor(arkInstanceFieldRef: ArkInstanceFieldRef) {
        super(arkInstanceFieldRef);
        this.parse(arkInstanceFieldRef);
    }

    private parse(instanceFieldRef: ArkInstanceFieldRef) {
        this.json.setBase(parseArkValue(instanceFieldRef.getBase()));
    }
}

class ArkParameterRefValue extends AbstractRefValue {
    constructor(arkParameterRef: ArkParameterRef) {
        super(arkParameterRef);
        this.parse(arkParameterRef);
    }

    private parse(arkParameterRef: ArkParameterRef) {
        this.json.setIndex(arkParameterRef.getIndex());
    }
}

class ConstantValue extends BasicValue {
    constructor(constant: Constant) {
        super(constant);
        this.parse(constant);
    }

    private parse(constant: Constant) {
        this.json.setValue(constant.getValue())
    }
}

class AbstractExprValue extends BasicValue {
    constructor(abstractExpr: AbstractExpr) {
        super(abstractExpr);
    }
}

class AbstractInvokeExprValue extends AbstractExprValue {
    constructor(abstractInvokeExpr: AbstractInvokeExpr) {
        super(abstractInvokeExpr);
        this.json.setName(abstractInvokeExpr.getMethodSignature().getMethodSubSignature().getMethodName());
        let argsValue = []
        for(const value of abstractInvokeExpr.getArgs()){
            argsValue.push(parseArkValue(value));
        }
        this.json.setArgs(argsValue);
    }
}

class ArkInstanceInvokeExprValue extends AbstractInvokeExprValue {
    constructor(instanceInvokeExpr: ArkInstanceInvokeExpr) {
        super(instanceInvokeExpr);
        this.parse(instanceInvokeExpr);
    }

    private parse(instanceInvokeExpr: ArkInstanceInvokeExpr) {
        this.json.setBase(parseArkValue(instanceInvokeExpr.getBase()));
    }
}

class ArkPtrInvokeExprValue extends AbstractInvokeExprValue {
    constructor(ptrInvokeExpr: ArkPtrInvokeExpr) {
        super(ptrInvokeExpr);
        this.parse(ptrInvokeExpr);
    }

    private parse(ptrInvokeExpr: ArkPtrInvokeExpr) {
        this.json.setFunPtrLocal(parseArkValue(ptrInvokeExpr.getFuncPtrLocal()));
    }
}

class ArkNewExprValue extends AbstractExprValue {
    constructor(arkNewExpr: ArkNewExpr) {
        super(arkNewExpr);
        this.parse(arkNewExpr);
    }

    private parse(arkNewExpr: ArkNewExpr) {
        this.json.setClassType(parseArkType(arkNewExpr.getClassType()));
    }
}

class ArkNewArrayExprValue extends AbstractExprValue {
    constructor(arkNewArrayExpr: ArkNewArrayExpr) {
        super(arkNewArrayExpr);
        this.parse(arkNewArrayExpr);
    }

    private parse(arkNewArrayExpr: ArkNewArrayExpr) {
        this.json.setBaseType(parseArkType(arkNewArrayExpr.getBaseType()));
        this.json.setSize(parseArkValue(arkNewArrayExpr.getSize()));
    }
}

class ArkDeleteExprValue extends AbstractExprValue {
    constructor(arkDeleteExpr: ArkDeleteExpr) {
        super(arkDeleteExpr);
        this.parse(arkDeleteExpr);
    }

    private parse(arkDeleteExpr: ArkDeleteExpr) {
        this.json.setField(parseArkValue(arkDeleteExpr.getField()))
    }
}

class ArkAwaitExprValue extends AbstractExprValue {
    constructor(arkAwaitExpr: ArkAwaitExpr) {
        super(arkAwaitExpr);
        this.parse(arkAwaitExpr);
    }

    private parse(arkAwaitExpr: ArkAwaitExpr) {
        this.json.setPromise(parseArkValue(arkAwaitExpr.getPromise()))
    }
}

class ArkYieldExprValue extends AbstractExprValue {
    constructor(arkYieldExpr: ArkYieldExpr) {
        super(arkYieldExpr);
        this.parse(arkYieldExpr);
    }

    private parse(arkYieldExpr: ArkYieldExpr) {
        this.json.setYieldValue(parseArkValue(arkYieldExpr.getYieldValue()))
    }
}

class AbstractBinopExprValue extends AbstractExprValue {
    constructor(abstractBinopExpr: AbstractBinopExpr) {
        super(abstractBinopExpr);
        this.parse(abstractBinopExpr);
    }

    private parse(abstractBinopExpr: AbstractBinopExpr) {
        this.json.setOp1(parseArkValue(abstractBinopExpr.getOp1()));
        this.json.setOp2(parseArkValue(abstractBinopExpr.getOp2()));
        this.json.setOperator(abstractBinopExpr.getOperator());
    }
}

class ArkTypeOfExprValue extends AbstractExprValue {
    constructor(arkTypeOfExpr: ArkTypeOfExpr) {
        super(arkTypeOfExpr);
        this.parse(arkTypeOfExpr);
    }

    private parse(arkTypeOfExpr: ArkTypeOfExpr) {
        this.json.setOp(parseArkValue(arkTypeOfExpr.getOp()))
    }
}

class ArkInstanceOfExprValue extends AbstractExprValue {
    constructor(arkInstanceOfExpr: ArkInstanceOfExpr) {
        super(arkInstanceOfExpr);
        this.parse(arkInstanceOfExpr);
    }

    private parse(arkInstanceOfExpr: ArkInstanceOfExpr) {
        this.json.setOp(parseArkValue(arkInstanceOfExpr.getOp()));
        this.json.setCheckType(parseArkType(arkInstanceOfExpr.getCheckType()));
    }
}

class ArkCastExprValue extends AbstractExprValue {
    constructor(arkCastExpr: ArkCastExpr) {
        super(arkCastExpr);
        this.parse(arkCastExpr);
    }

    private parse(arkCastExpr: ArkCastExpr) {
        this.json.setOp(parseArkValue(arkCastExpr.getOp()))
    }
}

class ArkPhiExprValue extends AbstractExprValue {
    constructor(arkPhiExpr: ArkPhiExpr) {
        super(arkPhiExpr);
        this.parse(arkPhiExpr);
    }

    private parse(arkPhiExpr: ArkPhiExpr) {
        let argsValue = [];
        for(const arg of arkPhiExpr.getArgs()){
            argsValue.push(parseArkValue(arg));
        }
        this.json.setArgs(argsValue);
    }
}

class ArkUnopExprValue extends AbstractExprValue {
    constructor(arkUnopExpr: ArkUnopExpr) {
        super(arkUnopExpr);
        this.parse(arkUnopExpr);
    }

    private parse(arkUnopExpr: ArkUnopExpr) {
        this.json.setOp(parseArkValue(arkUnopExpr.getOp()));
        this.json.setOperator(arkUnopExpr.getOperator());
    }
}
